﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using NthDownload.Contracts;
using System.Net;
using NthDownload.Contracts.Plugins.Downloads;
using System.ComponentModel.Composition;
using NthDownload.Composition;
using System.Linq;

namespace NthDownload.DownloadPlugins.Http
{
    public class HttpDownload : IDownload
    {
        #region Class Members

        private readonly object _syncContext = new object();

        private string _guid;
        private string _parentGUID;
        private string _path;
        private bool _canResume;
        private bool _canQueue;
        private bool _hasSize;
        private bool _isInitialized;
        private string _destinationFolder;
        private string _destinationPath;
        private string _description;
        private long _size;
        private string _fileName;
        private int _position;
        private int _chunkSize;
        private int _retryCount;
        private long _downloadedAmount;
        private List<DownloadSegment> _segments;
        private DateTime _dateCreated;
        private DateTime _dateCompleted;
        private TimeSpan _timeElapsed;
        private DownloadStatuses _status;

        #endregion Class Members

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion INotifyPropertyChanged

        #region Constructors

        public HttpDownload(string path)
        {
            Path = path;
            Segments = new List<DownloadSegment>();

            SatisfyImports();
        }

        #endregion Constructors

        #region Properties

        public string GUID
        {
            get
            {
                return _guid;
            }
            set
            {
                _guid = value;
                OnPropertyChanged("GUID");
            }
        }

        public string Path
        {
            get
            {
                return _path;
            }
            set
            {
                _path = value;
                OnPropertyChanged("Path");
            }
        }

        public string ParentGUID
        {
            get
            {
                return _parentGUID;
            }
            set
            {
                _parentGUID = value;
                OnPropertyChanged("ParentGUID");
            }
        }

        public bool CanResume
        {
            get
            {
                return _canResume;
            }
            set
            {
                _canResume = value;
                OnPropertyChanged("CanResume");
            }
        }

        public bool CanQueue
        {
            get
            {
                return _canQueue;
            }
            set
            {
                _canQueue = value;
                OnPropertyChanged("CanQueue");   
            }
        }

        public bool HasSize
        {
            get
            {
                return _hasSize;
            }
            set
            {
                _hasSize = value;
                OnPropertyChanged("HasSize");
            }
        }

        public bool IsInitialized
        {
            get
            {
                return _isInitialized;
            }
            set
            {
                _isInitialized = value;
                OnPropertyChanged("IsInitialized");
            }
        }

        public string DestinationFolder
        {
            get
            {
                return _destinationFolder;
            }
            set
            {
                _destinationFolder = value;
                OnPropertyChanged("DestinationFolder");
            }
        }

        public string DestinationPath
        {
            get
            {
                return _destinationPath;
            }
            set
            {
                _destinationPath = value;
                OnPropertyChanged("DestinationPath");
            }
        }

        public string Description
        {
            get
            {
                return _description;
            }
            set
            {
                _description = value;
                OnPropertyChanged("Description");
            }
        }

        public long Size
        {
            get
            {
                return _size;
            }
            set
            {
                _size = value;
                OnPropertyChanged("Size");
            }
        }

        public string FileName
        {
            get
            {
                return _fileName;
            }
            set
            {
                _fileName = value;
                OnPropertyChanged("FileName");
            }
        }

        public int Position
        {
            get
            {
                return _position;
            }
            set
            {
                _position = value;
                OnPropertyChanged("Position");
            }
        }
        
        public int ChunkSize
        {
            get
            {
                return _chunkSize;
            }
            set
            {
                _chunkSize = value;
                OnPropertyChanged("ChunkSize");
            }
        }

        public int RetryCount
        {
            get
            {
                return _retryCount;
            }
            set
            {
                _retryCount = value;
                OnPropertyChanged("RetryCount");
            }
        }

        public long DownloadedAmount
        {
            get
            {
                return _downloadedAmount;
            }
            set
            {
                _downloadedAmount = value;
                OnPropertyChanged("DownloadedAmount");
            }
        }

        public List<DownloadSegment> Segments
        {
            get
            {
                return _segments;
            }
            set
            {
                _segments = value;
                OnPropertyChanged("Segments");
            }
        }

        public DateTime DateCreated
        {
            get
            {
                return _dateCreated;
            }
            set
            {
                _dateCreated = value;
                OnPropertyChanged("DateCreated");
            }
        }

        public DateTime DateCompleted
        {
            get
            {
                return _dateCompleted;
            }
            set
            {
                _dateCompleted = value;
                OnPropertyChanged("DateCompleted");
            }
        }

        public TimeSpan TimeElapsed
        {
            get
            {
                return _timeElapsed;
            }
            set
            {
                _timeElapsed = value;
                OnPropertyChanged("TimeElapsed");
            }
        }

        public DownloadStatuses Status
        {
            get
            {
                return _status;
            }
            set
            {
                _status = value;
                OnPropertyChanged("Status");
            }
        }

        /// <summary>
        /// Current Settings
        /// </summary>
        [Import(AllowRecomposition = true)]
        public HttpSettings Settings { get; set; }

        #endregion Properties

        #region Protected Methods

        /// <summary>
        /// Satisfies imports for the current view model
        /// </summary>
        protected void SatisfyImports()
        {
            ImportService.TrySatisfyImports(this);
        }

        #endregion Protected Methods

        #region Methods

        public IDownloadStream OpenStream(long startPosition, long endPosition)
        {
            lock (_syncContext)
            {
                //TODO Tidy how the credentials are generated
                CredentialCache myCache = new CredentialCache();
                foreach (var credentials in Settings.Credentials)
                {
                    myCache.Add(new Uri(credentials.Domain), "Basic", new NetworkCredential(credentials.Username, credentials.Password));
                    myCache.Add(new Uri(credentials.Domain), "Digest", new NetworkCredential(credentials.Username, credentials.Password));
                }

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Path);
                request.AddRange(startPosition);
                request.Credentials = myCache;

                //TODO Add setting for time out
                //request.Timeout = 1000;
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                return new HttpDownloadStream(response.GetResponseStream(), response);
            }
        }

        #endregion Methods
    }
}
